namespace Resharper.Abstractions.CodeDom.Statements.ThrowStatements
{
  using JetBrains.Annotations;
  using JetBrains.ReSharper.Psi;
  using JetBrains.ReSharper.Psi.CSharp.Tree;
  using JetBrains.ReSharper.Psi.Tree;
  using Resharper.Abstractions.CodeDom.Expressions;
  using Resharper.Abstractions.CodeDom.Statements.TryStatements;

  /// <summary>Defines the <see cref="TryStatement"/> class.</summary>
  public class ThrowStatement : Statement
  {
    #region Constructors and Destructors

    /// <summary>Initializes a new instance of the <see cref="TryStatement"/> class.</summary>
    /// <param name="statement">The statement.</param>
    internal ThrowStatement([NotNull] IThrowStatement statement) : base(statement)
    {
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets the name of the escaped exception type.
    /// </summary>
    [NotNull]
    public string EscapedExceptionTypeName
    {
      get
      {
        return this.ExceptionTypeName.Replace("<", "{").Replace(">", "}");
      }
    }

    /// <summary>
    /// Gets the exception message.
    /// </summary>
    [NotNull]
    public string ExceptionMessage
    {
      get
      {
        var expression = this.InternalThrowStatement.Exception;
        if (expression == null)
        {
          return string.Empty;
        }

        var argumentsOwner = expression as IArgumentsOwner;
        if (argumentsOwner == null)
        {
          return "<c>" + this.ExceptionTypeName + "</c>";
        }

        var result = string.Empty;

        foreach (var argument in argumentsOwner.Arguments)
        {
          var csharpArgument = argument as ICSharpArgument;
          if (csharpArgument == null)
          {
            continue;
          }

          if (csharpArgument.Kind != ParameterKind.VALUE)
          {
            continue;
          }

          var value = csharpArgument.Value.ConstantValue;
          if (!value.IsString())
          {
            continue;
          }

          var stringValue = value.Value as string;
          if (string.IsNullOrEmpty(stringValue))
          {
            continue;
          }

          result = stringValue;

          break;
        }

        if (this.ExceptionTypeName == "System.ArgumentOutOfRangeException")
        {
          result = string.Format("<c>{0}</c> is out of range.", result);
        }

        return result;
      }
    }

    /// <summary>
    /// Gets the type of the exception.
    /// </summary>
    [NotNull]
    public string ExceptionTypeName
    {
      get
      {
        var expression = this.InternalThrowStatement.Exception;
        return expression != null ? ExpressionFactory.GetExpression(expression).TypeName : this.GettRethrownExceptionTypeName();
      }
    }

    /// <summary>
    /// Gets the internal for statement.
    /// </summary>
    [NotNull]
    internal IThrowStatement InternalThrowStatement
    {
      get
      {
        return (IThrowStatement)this.InternalTreeNode;
      }
    }

    #endregion

    #region Public Methods

    /// <summary>Determines whether this instance is rethrow.</summary>
    /// <returns><c>true</c> if this instance is rethrow; otherwise, <c>false</c>.</returns>
    public bool IsRethrow()
    {
      return this.InternalThrowStatement.Exception == null;
    }

    #endregion

    #region Methods

    /// <summary>Getts the name of the rethrown exception type.</summary>
    /// <returns>Returns the rethrown exception type name.</returns>
    [NotNull]
    private string GettRethrownExceptionTypeName()
    {
      var catchClause = this.InternalThrowStatement.GetContainingNode<ICatchClause>();
      if (catchClause == null)
      {
        return string.Empty;
      }

      var type = catchClause.ExceptionType;
      if (type == null)
      {
        return string.Empty;
      }

      return type.GetLongPresentableName(this.InternalThrowStatement.Language);
    }

    #endregion
  }
}